﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Ice.Toolkit.Data.Entity;

namespace Ice.Toolkit.Data.Sql
{
    internal class SqlWhere<T> where T : EntityBase<T>, new()
    {
        public List<String> Columns { get; private set; }

        public List<Object> Values { get; private set; }

        public String SqlWhereText { get; private set; }

        public Expression<Func<T, Boolean>> SqlExpression { get; private set; }

        public SqlWhere(Expression<Func<T, Boolean>> sqlWhere)
        {
            SqlExpression = sqlWhere;
            Columns = new List<string>();
            Values = new List<object>();
            Compile();
        }

        public void Compile()
        {
            var body = SqlExpression.Body as BinaryExpression;

            SqlWhereText = Decomposition(body);
        }

        private String Decomposition(BinaryExpression body)
        { 
            string sqlText = String.Empty;

            if (body == null) return sqlText;

            if ((body.Left is BinaryExpression) && (body.Right is BinaryExpression))
            {
                sqlText += Decomposition(body.Left as BinaryExpression);
                sqlText += GetOperator(body.NodeType);
                sqlText += Decomposition(body.Right as BinaryExpression);
            }
            else if ((body.Left is BinaryExpression) && (body.Right) is ConstantExpression)
            {
                var lExp = body.Left as BinaryExpression;
                var filedName = String.Empty;

                sqlText += "(";
                sqlText += ExpressionString(lExp.Left, lExp.Right, lExp.NodeType, out filedName);
                sqlText += " " + GetOperator(body.NodeType);
                sqlText += " @" + filedName + ")";

                this.Columns.Add(filedName);
                this.Values.Add((body.Right as ConstantExpression).Value);
            }
            else
            {
                sqlText += ExpressionString(body.Left, body.Right, body.NodeType);
            }

            return sqlText; 
        }

        private String ExpressionString(Expression left, Expression right, ExpressionType nodeType, out string fieldName)
        {
            var expStr = String.Empty;
            fieldName = String.Empty;

            if (left is BinaryExpression)
            { 
                var lexp = left as BinaryExpression;

                expStr += ExpressionString(lexp.Left, lexp.Right, lexp.NodeType, out fieldName);
            }
            else if (left is MemberExpression)
            {
                fieldName = (left as MemberExpression).Member.Name;
                expStr += "[" + fieldName + "]";
            }
            
            expStr += GetOperator(nodeType);
            expStr += (right as ConstantExpression).Value;
          
            return expStr;
        }

        private String ExpressionString(Expression left, Expression right, ExpressionType nodeType)
        {
            var leftExpression = left as MemberExpression;
            var rightExpression = right as ConstantExpression;

           
            if (rightExpression != null)
            {
                if (rightExpression.Value.ToString() == "NULL" && nodeType == ExpressionType.Equal)
                {
                    return String.Format(" ([{0}] IS NULL) ", leftExpression.Member.Name); 
                }
                else if (rightExpression.Value.ToString() == "NULL" && nodeType == ExpressionType.NotEqual)
                {
                    return String.Format(" ([{0}] IS NOT NULL) ", leftExpression.Member.Name);
                }
                else
                {
                    this.Columns.Add(leftExpression.Member.Name);
                    this.Values.Add(rightExpression.Value);

                    return String.Format(" ([{0}] {1} @{2}) ", leftExpression.Member.Name, GetOperator(nodeType), leftExpression.Member.Name);
                }
            }
            else
            {
                if (right is MemberExpression)
                {
                    return String.Format(" ([{0}] {1} GETDATE()) ", leftExpression.Member.Name, GetOperator(nodeType));
                }
            }

            return String.Empty;
        }

        public string GetOperator(ExpressionType expType)
        {
            switch (expType)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " AND ";
                case ExpressionType.Equal:
                    return " =";
                case ExpressionType.GreaterThan:
                    return " >";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " OR ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return "+";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return "-";
                case ExpressionType.Divide:
                    return "/";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return "*";
                default:
                    return null;
            }

        }
    }
}
